//
//  OntologyTest.cpp
//  IncrementalAlgorithm
//
//  Created by Roman Kutlak on N_CONCEPTS*2/03/2011.
//  Copyright 2011 Roman Kutlak. All rights reserved.
//

#include <sstream>
#include <iostream>
#include <list>

#include "gtest/gtest.h"

#include "Ontology.h"

using namespace std;
using namespace kutlak::roman::gre;

#define N_CONCEPTS 11

// Tests the default c'tor.
TEST(Ontology, Lifecycle) 
{
    Ontology o;
    EXPECT_EQ(0, o.Size());
    
    Ontology o2("/Users/roman/Projects/NLP/gre/resources/ontology.nt");
    EXPECT_EQ(N_CONCEPTS, o2.Size());
    
    Ontology o3(o2);
    EXPECT_EQ(N_CONCEPTS, o3.Size());
    
    Ontology o4(std::move(o2));
    EXPECT_EQ(N_CONCEPTS, o4.Size());
    EXPECT_EQ(0, o2.Size());
    
    Ontology o5;
    EXPECT_NE(o4, o5);
    o5 = o4;
    EXPECT_EQ(o4, o5);
    
    EXPECT_EQ(o3, o4);
    o3.Clear();
    EXPECT_NE(o3, o4);
    
}

TEST(Ontology, LoadFileNt) 
{
    Ontology o;
    EXPECT_EQ(0, o.Size());
    
    o.OpenFile("/Users/roman/Projects/NLP/gre/resources/ontology.nt");
    EXPECT_EQ(N_CONCEPTS, o.Size());
    
    o.OpenFile("/Users/roman/Projects/NLP/gre/resources/ontology.nt");
    EXPECT_EQ(N_CONCEPTS*2, o.Size());
    
#ifdef DEBUG
    cout << "saving model as RDF nt" << endl;
#endif
    
    o.SaveFile("/Users/roman/tmp/kb.nt");
}

TEST(Ontology, Enquiry) 
{
    Ontology o("/Users/roman/Projects/NLP/gre/resources/dbpedia.nt");
    EntityPtrT e(new Entity());
    e->AddProperty(Ontology::kPersonType);
    EXPECT_EQ(true, o.IsNamedEntity(e));
    
    Property something(Ontology::kType, "something");
    e->ClearProperties();
    e->SetType(something);
    EXPECT_EQ(something, e->Type());
    EXPECT_EQ(false, o.IsNamedEntity(e));
    
    Vindex_t almaMater = Voc::Instance()
    .Index("http://dbpedia.org/ontology/almaMater");
    
    EXPECT_EQ(true, o.RangeIsNamedEntity(almaMater));
    EXPECT_EQ(false, o.RangeIsNamedEntity(Voc::Instance().NumWords()));
    EXPECT_EQ(false, o.RangeIsNamedEntity(0));
    
    EXPECT_EQ(true, o.IsRelation(Property(almaMater)));
    EXPECT_EQ(true, o.IsRelation(almaMater));
    EXPECT_EQ(false, o.IsRelation(0));
}


TEST(Ontology, Search) 
{
    Ontology::SetDefault
    (pOntology
     (new Ontology("/Users/roman/Projects/NLP/gre/resources/ontology.nt")));
    Ontology& o = *Ontology::Default();
    
    // default values are "", "", "" -> return all triples
    Ontology::ContainerT tmp = o.Search();
    EXPECT_EQ(11, tmp.size());
    
    // search for triples that have Person as a subject
    tmp = o.Search("furniture", "");
    EXPECT_EQ(1, tmp.size());
    
    // no info about Person
    tmp = o.Search("Person", "");
    EXPECT_EQ(0, tmp.size());
    
    // find all subclass definitions
    tmp = o.Search("", Ontology::kSubClassOf);
    EXPECT_EQ(9, tmp.size());
    
    // wrong format
    tmp = o.Search("", "SubClassOf");
    EXPECT_EQ(0, tmp.size());
    
    tmp = o.Search("", "", "http://www.w3.org/2002/07/owl#Thing");
    EXPECT_EQ(3, tmp.size());
    
    // wrong format
    tmp = o.Search("", "", "Thing");
    EXPECT_EQ(0, tmp.size());
}


TEST(Ontology, LessSpecificValues) 
{
    Voc& vocab = Voc::Instance();
    
    Ontology& o2 = *Ontology::Default();
    
    Property val = o2.LessSpecificValue(Property(Ontology::kType, "funnyDesk"));
    
    EXPECT_EQ(vocab.Index("desk"), val.Value());
    
    val = o2.LessSpecificValue(Property(Ontology::kType, "desk"));
    EXPECT_EQ(vocab.Index("furniture"), val.Value());
}

TEST(Ontology, MoreSpecificValuess) 
{
    Ontology& o2 = *Ontology::Default();
    
    std::vector<Property> vals = o2.MoreSpecificValues(Property(Ontology::kType, "furniture"));
    EXPECT_EQ(3, vals.size());
    auto end = vals.end();
    auto it = find(vals.begin(), vals.end(), Property(Ontology::kType, "desk"));
    EXPECT_NE(end, it);
    auto it2 = find(vals.begin(), vals.end(), Property(Ontology::kType, "chair"));
    EXPECT_NE(end, it2);
    auto it3 = find(vals.begin(), vals.end(), Property(Ontology::kType, "table"));
    EXPECT_NE(end, it3);
    
    vals = o2.MoreSpecificValues(Property(Ontology::kType, "desk"));
    EXPECT_EQ(1, vals.size());
    auto it4 = find(vals.begin(), vals.end(), Property(Ontology::kType, "funnyDesk"));
    EXPECT_NE(vals.end(), it4);
}

TEST(Ontology, EquivalentClass) 
{
    Ontology& o2 = *Ontology::Default();
    Voc& vocab = Voc::Instance();
    
    Vindex_t foaf = vocab.Index("http://xmlns.com/foaf/0.1/Person");
    Vindex_t schema = vocab.Index("http://schema.org/Person");
    Vindex_t dbp = Ontology::kPerson;
    
    EXPECT_EQ(true, o2.IsEquivalentClass(foaf, foaf));
    EXPECT_EQ(true, o2.IsEquivalentClass(foaf, schema));    
    EXPECT_EQ(true, o2.IsEquivalentClass(schema, foaf));
    EXPECT_EQ(true, o2.IsEquivalentClass(schema, dbp)); 
    EXPECT_EQ(true, o2.IsEquivalentClass(dbp, schema));
    EXPECT_EQ(true, o2.IsEquivalentClass(dbp, foaf));
    EXPECT_EQ(true, o2.IsEquivalentClass(foaf, dbp));
}

TEST(Ontology, Subsumes) 
{
    Ontology& o2 = *Ontology::Default();
    
    // depth 0
    EXPECT_EQ(true, o2.Subsumes(Property(Ontology::kType, "chair"), 
                                Property(Ontology::kType, "chair")));
    
    // depth 1
    EXPECT_EQ(true, o2.Subsumes(Property(Ontology::kType, "furniture"), 
                                Property(Ontology::kType, "desk")));
    
    // depth 2
    EXPECT_EQ(true, o2.Subsumes(Property(Ontology::kType, "furniture"), 
                                Property(Ontology::kType, "funnyDesk")));
    
    // reverse 1
    EXPECT_EQ(false, o2.Subsumes(Property(Ontology::kType, "desk"), 
                                 Property(Ontology::kType, "furniture")));
    
    // reverse 2
    EXPECT_EQ(false, o2.Subsumes(Property(Ontology::kType, "funnyDesk"), 
                                 Property(Ontology::kType, "furniture")));
    
    
    EXPECT_EQ(1, o2.Subsumes(Property(Ontology::kThingType), 
                             Property(Ontology::kType, "dark")));
    
    //owl:Thing subsumes everything
    EXPECT_EQ(true, o2.Subsumes(Property(Ontology::kThingType), 
                                Property(Ontology::kPersonType)));
    
    EXPECT_EQ(false, o2.Subsumes(Property(Ontology::kPersonType), 
                                 Property(Ontology::kThingType)));
    
    EXPECT_EQ(true, o2.Subsumes(Property(Ontology::kType, "http://schema.org/Person"), 
                                Property(Ontology::kType, "http://xmlns.com/foaf/0.1/Person")));
    
    EXPECT_EQ(true, o2.Subsumes(Property(Ontology::kType, "http://xmlns.com/foaf/0.1/Person"), 
                                Property(Ontology::kType, "http://schema.org/Person")));
    
    // this should be 'inferred' due to equivalent class foaf:Person and dbp:Person
    EXPECT_EQ(true, o2.Subsumes(Property(Ontology::kType, "http://schema.org/Person"), 
                                Property(Ontology::kPersonType)));
}


TEST(Ontology, BasicLevelValues) 
{
    Voc& vocab = Voc::Instance();
    Ontology& o2 = *Ontology::Default();
    
    Property val = o2.BasicLevelValue(Property(Ontology::kType, "funnyDesk"));
    
    EXPECT_EQ(vocab.Index("furniture"), val.Value());
}

TEST(Ontology, Serialisation) {
    stringstream good;
    stringstream bad;
    
    Ontology o("/Users/roman/Projects/NLP/gre/resources/ontology.nt");
    
    good << o;
    bad << "<bad format>";
    Ontology o2;
    
    EXPECT_NE(o, o2);
    
    try
    {
        bad >> o2;
        EXPECT_EQ(true, false); // the above should throw
    }
    catch (DeserialisationException& de)
    {
        cerr << de.what() << '\n';
    }
    
    EXPECT_NE(o, o2);
    
    try
    {
        good >> o2;
    }
    catch (DeserialisationException& de)
    {
        cerr << de.what();
        EXPECT_EQ(true, false); // the above should NOT throw
    }
    
    EXPECT_EQ(o, o2);
}
